package org.syntaxlisp.core.utils;

import org.apache.commons.collections4.CollectionUtils;

import java.util.*;
import java.util.function.Function;

public class ListTree<K extends ListTreeVal> {

    private final ListTreeNode<K, Boolean> rootEntry;

    private final Function<K, Integer> getKeyFunc;

    public ListTreeNode<K, Boolean> getRoot () {
        return rootEntry;
    }
    
    public ListTree(Function<K, Integer> getKeyFunc) {
        rootEntry = new ListTreeNode<>(null, null, null);
        this.getKeyFunc = getKeyFunc;
    }

    public boolean add (ListTreeNode<K, Boolean> lastNode) {
        return add(lastNode.getEntireKeyList());
    }

    public boolean add(List<K> keyList) {
        ListTreeNode<K, Boolean> endNode = putNodes(keyList);
        boolean prevNoAdded = endNode.getVal() == null;
        if (!prevNoAdded) {
            endNode.setVal(true);
/*            leafMap.computeIfAbsent(endNode.getNodeKey(), key -> new ArrayList<>())
                    .add(endNode);*/
        }
        return prevNoAdded;
    }

    private ListTreeNode<K, Boolean> putNodes (List<K> keyList) {
        if (!CollectionUtils.isEmpty(keyList)) {
            ListTreeNode<K, Boolean> curNode = rootEntry;
            for (K key : keyList) {
                curNode = curNode.putNextNode(key, getKeyFunc);
            }
            return curNode;
        } else {
            return rootEntry;
        }
    }

    public boolean contains(List<K> keyList) {
        Optional<ListTreeNode<K, Boolean>> node = getNode(keyList);
        return node.map(n -> n.val != null).orElse(false);
    }

    public Boolean get (List<K> keyList) {
        Optional<ListTreeNode<K, Boolean>> node = getNode(keyList);
        return node.map(ListTreeNode::getVal).orElse(null);
    }

    private Optional<ListTreeNode<K, Boolean>> getNode (List<K> keyList) {
        ListTreeNode<K, Boolean> curNode = rootEntry;
        for (K k : keyList) {
            Optional<ListTreeNode<K, Boolean>> nextRes = curNode.getNextNode(k, getKeyFunc);
            if (nextRes.isPresent()) {
                curNode = nextRes.get();
            } else {
                return Optional.empty();
            }
        }
        return Optional.of(curNode);
    }


    private static List<Character> stringToCharList (String val) {
        List<Character> res = new ArrayList<>(val.length());
        for (char ch : val.toCharArray()) {
            res.add(ch);
        }
        return res;
    }

}
